package com.iuie.ns.datum.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.github.pagehelper.PageHelper;
import com.iuie.ns.datum.mapper.UserExpenseInfoMapper;
import com.iuie.ns.datum.mapper.UserExpenseInstalmentInfoMapper;
import com.iuie.ns.datum.mapper.UserExpenseReportMapper;
import com.iuie.ns.datum.service.IExpenseService;
import com.iuie.ns.datum.service.IFileInfoService;
import com.iuie.ns.datum.service.IFileService;
import com.iuie.ns.datum.vo.FileInfoVO;
import com.iuie.ns.datum.vo.MthInstalmentDetailVO;
import com.iuie.ns.datum.vo.UserExpenseInfoVO;
import com.iuie.ns.datum.vo.UserExpenseInstalmentInfoVO;
import com.iuie.ns.datum.vo.UserExpenseStatisticInfoVO;
import com.iuie.ns.frame.exception.ServiceException;
import com.iuie.ns.frame.utils.DateUtils;
import com.iuie.ns.frame.utils.ListUtils;
import com.iuie.ns.frame.utils.ShiroUtils;
import com.iuie.ns.frame.utils.StringUtils;
import com.iuie.ns.frame.web.dataset.vo.Page;
import com.iuie.ns.system.service.code.ISysCodeInfoService;
import com.iuie.ns.system.utils.QuickReportUtils;
import com.iuie.ns.system.utils.callback.QucikReportCallback;
import com.iuie.ns.system.utils.callback.QuickPieChartCallback;
import com.iuie.ns.system.vo.user.SysUserVO;

@Service("expenseService")
public class ExpenseServiceImpl implements IExpenseService {
	
	@Resource
	private UserExpenseInfoMapper expenseMapper;
	
	@Resource
	private UserExpenseInstalmentInfoMapper expenseInstalmentInfoMapper;
	
	@Resource
	private UserExpenseReportMapper expenseReportMapper;
	
	@Resource
	private ISysCodeInfoService codeInfoService;
	
	@Resource
	private IFileService fileService;
	
	@Resource
	private IFileInfoService fileInfoService;
	
	private static final Logger logger = LoggerFactory.getLogger(ExpenseServiceImpl.class);
	
	@Override
	public List<UserExpenseInfoVO> findByPage(UserExpenseInfoVO condition, Page page) {
		PageHelper.startPage(page.getPage(), page.getRows(), true);
		
		SysUserVO user = (SysUserVO) ShiroUtils.getCurrentUser();
		condition.setUserId(user.getUserId());
		
		List<UserExpenseInfoVO> list = expenseMapper.findByCondition(condition);
		page.loadData(list);
		return list;
	}

	@Override
	public void addOne(UserExpenseInfoVO item, String targetId) throws IOException {
		item.setId(StringUtils.uuid());
		
		this.validate(item);
		
		SysUserVO user = (SysUserVO) ShiroUtils.getCurrentUser();
		String userId = user.getUserId();
		String userName = user.getUserName();
		item.setUserId(userId);
		item.setUserName(userName);
		item.setCreateTime(new Date());
		item.setUpdateTime(new Date());
		
		// 批量新增分期信息
		this.batchAddInstalmentRepayDetail(item);
		
		// 保存
		expenseMapper.addOne(item);
		
		// 处理附件信息
		if (StringUtils.isNotBlank(targetId)) {
			FileInfoVO condition = new FileInfoVO();
			condition.setTargetId(targetId);
			List<FileInfoVO> list = fileInfoService.findByTargetId(targetId);
			if (ListUtils.isNotEmpty(list)) {
				for (FileInfoVO fileItem: list) {
					fileService.letFileNameLegal(fileItem.getFileId());
					fileItem.setTargetId(item.getId());
					FileInfoVO fileInfo = fileInfoService.loadById(fileItem.getFileId());
					fileInfo.setTargetId(item.getId());
				}
			}
		}
	}
	
	private void validate(UserExpenseInfoVO item) {
		if (StringUtils.isBlank(item.getProductName())) {
			throw new ServiceException("商品名称不能为空！");
		}
		if (item.getConsumeTime() == null) {
			throw new ServiceException("消费时间不能为空！");
		}
		if (item.getProductAmount() == null) {
			throw new ServiceException("产品金额不能为空！");
		}
		Pattern pattern = Pattern.compile("^(([1-9][0-9]*)|(([0]\\.\\d{1,2}|[1-9][0-9]*\\.\\d{1,2})))$");
		Matcher matcher = pattern.matcher(String.valueOf(item.getProductAmount()));
		if (!matcher.matches()) {
			throw new ServiceException("产品金额为不超过2位小数的正数！");
		}
		if (item.getConsumeAmount() == null) {
			item.setConsumeAmount(item.getProductAmount());
		}
		if (StringUtils.isBlank(item.getConsumeChannel())) {
			throw new ServiceException("消费渠道不能为空！");
		}
		if (StringUtils.isBlank(item.getConsumeAddress())) {
			throw new ServiceException("消费地点不能为空！");
		}
		if (StringUtils.isBlank(item.getConsumeType())) {
			throw new ServiceException("消费类型不能为空！");
		}
		if (StringUtils.isBlank(item.getIsCountTime())) {
			throw new ServiceException("请确认是否为计时商品！");
		}
		if (StringUtils.isBlank(item.getRecommend())) {
			// 默认为一般
			item.setRecommend("02");
		}
		if (StringUtils.isBlank(item.getIsInstalment())) {
			// 默认为否
			item.setIsInstalment("02");
		}
		if (StringUtils.equals(item.getIsInstalment(), "01")) {
			// 分期商品
			// 分期期数和月还款额不能为空
			if (item.getInstallNum() == null || item.getInstallNum() == 0) {
				throw new ServiceException("分期商品的分期期数不能为空！");
			}
			if (item.getMthRepayAmount() == null) {
				throw new ServiceException("分期商品的月还款额不能为空！");
			}
			if (item.getMthRepayDate() == null) {
				throw new ServiceException("月还款日不能为空！");
			}
			if (item.getMthRepayDate() < 1 || item.getMthRepayDate() > 31) {
				throw new ServiceException("月还款日在[0-31]之间！");
			}
			if (item.getFirstRepayDate() == null) {
				throw new ServiceException("首次还款日不能为空！");
			}
			Matcher matchMthRepayAmount = pattern.matcher(String.valueOf(item.getMthRepayAmount()));
			if (!matchMthRepayAmount.matches()) {
				throw new ServiceException("月还款额为不超过2位小数的正数！");
			}
		}
	}
	
	// 保存分期信息
	@Override
	public void batchAddInstalmentRepayDetail(UserExpenseInfoVO item) {
		if (item == null || StringUtils.equals(item.getIsInstalment(), "02")) {
			// 未分期
			return ;
		}
		// 循环创建分期详情
		int instalNum = item.getInstallNum();
		Date lastRepayDate = item.getFirstRepayDate(); // 首次还款日
		double mthRepayAmount = item.getMthRepayAmount(); // 每月还款金额
		int mthRepayDate = item.getMthRepayDate(); // 每月还款日
		
		List<UserExpenseInstalmentInfoVO> list = new ArrayList<>();
		for (int i = 0; i < instalNum; i ++) {
			UserExpenseInstalmentInfoVO info = new UserExpenseInstalmentInfoVO();
			info.setExpenseId(item.getId());
			info.setId(StringUtils.uuid());
			info.setRepayNum(i + 1);
			info.setRepayDate(this.getRepayDate(lastRepayDate, i, mthRepayDate));
			info.setRepayAmount(mthRepayAmount);
			info.setUserId(item.getUserId());
			info.setUserName(item.getUserName());
			info.setCreateTime(new Date());
			info.setIsRepay("02"); // 02：未还
			list.add(info);
		}
		if (list.size() > 0 ) {
			expenseInstalmentInfoMapper.batchAddInstalmentRepayDetail(list);
		}
	}
	
	private Date getRepayDate(Date lastRepayDate, int i, int mthRepayDate) {
		final String pattern = "yyyy-MM-dd";
		String strLastRepayDate = DateUtils.formatDate(lastRepayDate, pattern);
		if (i > 0) {
			strLastRepayDate = DateUtils.formatDate(strLastRepayDate, "yyyy-MM") + "-" + mthRepayDate;
		}
		String strRepayDate = DateUtils.addMonth(strLastRepayDate, i, pattern);
		return DateUtils.stringToDate(strRepayDate, pattern);
	}
	
	@Override
	public void updateOne(UserExpenseInfoVO item, String id, String saveInstalmentDetail) {
		item.setId(id);
		
		this.validate(item);
		
		// 设置更新时间
		item.setUpdateTime(new Date());
		expenseMapper.updateOne(item);
		
		SysUserVO user = (SysUserVO) ShiroUtils.getCurrentUser();
		String userId = user.getUserId();
		String userName = user.getUserName();
		item.setUserId(userId);
		item.setUserName(userName);
		
		// 分期信息
		if (StringUtils.equals(saveInstalmentDetail, "1")) {
			this.deleteByExpenseId(id);
			this.batchAddInstalmentRepayDetail(item);
		}
	}

	@Override
	public UserExpenseInfoVO findById(String id) {
		if (StringUtils.isBlank(id)) {
			throw new ServiceException("查询失败，id为空！");
		}
		return expenseMapper.findById(id);
	}

	@Override
	public Integer deleteById(String id) {
		if (StringUtils.isBlank(id)) {
			throw new ServiceException("删除失败，id为空！");
		}
		UserExpenseInfoVO expenseInfo = this.findById(id);
		if (expenseInfo == null) {
			throw new ServiceException("删除失败，id=【" + id + "】对应的消费记录不存在！");
		}
		if (StringUtils.equals(expenseInfo.getIsInstalment(), "01")) {
			// 是分期消费
			// 删除分期信息
			this.deleteByExpenseId(id);
		}
		logger.info("正在删除消费记录，id = {}, 消费记录信息= {}", id, expenseInfo);
		
		// 删除附件
		fileService.deleteFileByTargetId(id);
		
		Integer count = expenseMapper.deleteById(id);
		return count;
	}
	
	@Override
	public UserExpenseStatisticInfoVO findStatisticInfo(UserExpenseInfoVO condition) {
		if (condition == null) {
			return null;
		}
		SysUserVO user = (SysUserVO) ShiroUtils.getCurrentUser();
		condition.setUserId(user.getUserId());
		
		// 查询当月消费总额
		Double sumConsumeAmount = expenseInstalmentInfoMapper.findSumConsumeAmount(condition); 
		
		// 查询当月未还贷款，需要关注的条件：消费时间，若消费时间为空，则查询出所有未还
		Double mthWaitRepayAmount = this.findMthWaitRepayAmount(condition);
		
		// 查询当月已还贷款，需要关注的条件：消费时间，若消费时间为空，则查询出所有已还
		Double mthHadRepayAmount = this.findMthHadRepayAmount(condition);
		
		UserExpenseStatisticInfoVO statisticInfo = new UserExpenseStatisticInfoVO();
		statisticInfo.setSumConsumeAmount(sumConsumeAmount);
		statisticInfo.setMthWaitRepayAmount(mthWaitRepayAmount);
		statisticInfo.setMthHadRepayAmount(mthHadRepayAmount);
		return statisticInfo;
	}
	
	@Override
	public Integer deleteByExpenseId(String expenseId) {
		logger.info("正在根据expenseId删除分期记录，expenseId={}", expenseId);
		return expenseInstalmentInfoMapper.deleteByExpenseId(expenseId);
	}
	
	@Override
	public List<UserExpenseInstalmentInfoVO> findAllInstalmentDetails(String expenseId, Page page) {
		if (StringUtils.isBlank(expenseId)) {
			throw new ServiceException("查询分期详情失败，[expenseId]为空！");
		}
		PageHelper.startPage(page.getPage(), page.getRows(), true);
		List<UserExpenseInstalmentInfoVO> list = expenseInstalmentInfoMapper.findAllInstalmentDetails(expenseId);
		page.loadData(list);
		return list;
	}
	
	@Override
	public void updateRepayAmount(String id, Double repayAmount) {
		if (StringUtils.isBlank(id)) {
			throw new ServiceException("更新还款金额失败，[id]为空！");
		}
		if (repayAmount == null) {
			throw new ServiceException("更新还款金额失败，[还款金额]为空！");
		}
		
		UserExpenseInstalmentInfoVO instalmentInfo = expenseInstalmentInfoMapper.findById(id);
		logger.info("正在更新还款金额，id={}, oldRepayAmount={}, newRepayAmount", new Object[]{id, instalmentInfo.getRepayAmount(), repayAmount});
		expenseInstalmentInfoMapper.updateRepayAmount(id, repayAmount, new Date());
	}
	
	@Override
	public void updateRemark(String id, String remark) {
		if (StringUtils.isBlank(id)) {
			throw new ServiceException("更新备注失败，[id]为空！");
		}
		expenseInstalmentInfoMapper.updateRemark(id, remark, new Date());
	}
	
	@Override
	public void updateRepayState(String id, String isRepay) {
		if (StringUtils.isBlank(id)) {
			throw new ServiceException("更新还款状态失败，[id]为空！");
		}
		
		// when isRepay is 01 then realRepayDate is current time, else then is null
		Date realRepayDate = null;
		if (StringUtils.equals(isRepay, "01")) {
			realRepayDate = new Date();
		}
		expenseInstalmentInfoMapper.updateRepayState(id, isRepay, new Date(), realRepayDate);
	}
	
	@Override
	public Double findMthWaitRepayAmount(UserExpenseInfoVO condition) {
		return expenseInstalmentInfoMapper.findMthInstalmentAmount(condition, "02");
	}
	
	@Override
	public Double findMthHadRepayAmount(UserExpenseInfoVO condition) {
		return expenseInstalmentInfoMapper.findMthInstalmentAmount(condition, "01");
	}
	
	@Override
	public Integer updateRealRepayDate(String id, Date realRepayDate) {
		return expenseInstalmentInfoMapper.updateRealRepayDate(id, realRepayDate, new Date());
	}
	
	@Override
	public List<MthInstalmentDetailVO> findMthInstalmentDetailByPage(MthInstalmentDetailVO condition, Page page, String date) {
		
//		SysUserVO user = (SysUserVO) ShiroUtils.getCurrentUser();
//		condition.setUserId(user.getUserId());
		
		PageHelper.startPage(page.getPage(), page.getRows(), true);
		List<MthInstalmentDetailVO> list = expenseInstalmentInfoMapper.findMthInstalmentDetailByCondition(condition, date);
		page.loadData(list);
		return list;
	}
	
	@Override
	public void loadConsumeSumReportData(Map<String, Object> params, HttpServletRequest request) {
		
		QuickReportUtils.handle(params, request, new QucikReportCallback() {

			@Override
			public List<Double> accordDay(List<String> queryParams, String userId) {
				return expenseReportMapper.findConsumeSumReportDataWithDay(queryParams, userId);
			}

			@Override
			public List<Double> accordWeek(List<String> queryParams, String userId) {
				return expenseReportMapper.findConsumeSumReportDataWithWeek(queryParams, userId);
			}

			@Override
			public List<Double> accordMonth(List<String> queryParams, String userId) {
				return expenseReportMapper.findConsumeSumReportDataWithMonth(queryParams, userId);
			}
		});
	}

	@Override
	public List<Double> findConsumeSumReportDataWithMonth(List<String> categories) {
		SysUserVO user = (SysUserVO) ShiroUtils.getCurrentUser();
		return expenseReportMapper.findConsumeSumReportDataWithMonth(categories, user.getUserId());
	}
	
	@Override
	public List<Double> findConsumeSumReportDataWithWeek(List<String> categories) {
		SysUserVO user = (SysUserVO) ShiroUtils.getCurrentUser();
		return expenseReportMapper.findConsumeSumReportDataWithWeek(categories, user.getUserId());
	}

	@Override
	public List<Double> findConsumeSumReportDataWithDay(List<String> categories) {
		SysUserVO user = (SysUserVO) ShiroUtils.getCurrentUser();
		return expenseReportMapper.findConsumeSumReportDataWithDay(categories, user.getUserId());
	}
	
	@Override
	public void loadConsumeTypeReportData(Map<String, Object> params, HttpServletRequest request) {
		QuickReportUtils.handlePieChart(params, request, new QuickPieChartCallback() {
			
			@Override
			public List<Map<String, String>> getReportData(String startTime, String endTime, String userId) {
				return expenseReportMapper.findConsumeTypeReportData(startTime, endTime, userId);
			}
		});
	}
	
	@Override
	public List<Map<String, String>> findConsumeTypeReportData(String startTime, String endTime, String userId) {
		return expenseReportMapper.findConsumeTypeReportData(startTime, endTime, userId);
	}

	@Override
	public void loadConsumeChannelReportData(Map<String, Object> params, HttpServletRequest request) {
		QuickReportUtils.handlePieChart(params, request, new QuickPieChartCallback() {
			
			@Override
			public List<Map<String, String>> getReportData(String startTime, String endTime, String userId) {
				return findConsumeChannelReportData(startTime, endTime, userId);
			}
		});
	}
	
	@Override
	public List<Map<String, String>> findConsumeChannelReportData(String startTime, String endTime, String userId) {
		return expenseReportMapper.findConsumeChannelReportData(startTime, endTime, userId);
	}
	
	@Override
	public void loadConsumeTypeAccordCountReportData(Map<String, Object> params, HttpServletRequest request) {
		QuickReportUtils.handlePieChart(params, request, new QuickPieChartCallback() {

			@Override
			public List<Map<String, String>> getReportData(String startTime, String endTime, String userId) {
				return expenseReportMapper.findConsumeTypeAccordCountReportData(startTime, endTime, userId);
			}
		});
	}
	
	@Override
	public List<Map<String, String>> findConsumeTypeAccordCountReportData(String startTime, String endTime, String userId) {
		return expenseReportMapper.findConsumeTypeAccordCountReportData(startTime, endTime, userId);
	}
	
	@Override
	public void loadConsumeChannelAccordCountReportData(Map<String, Object> params, HttpServletRequest request) {
		QuickReportUtils.handlePieChart(params, request, new QuickPieChartCallback() {

			@Override
			public List<Map<String, String>> getReportData(String startTime, String endTime, String userId) {
				return expenseReportMapper.findConsumeChannelAccordCountReportData(startTime, endTime, userId);
			}
		});
	}

}
